iT邦幫忙

DAY 22
1

SVG 與 D3.js系列 第 22

D3.js 資料數量增減

  • 分享至 

  • xImage
  •  

完整範例:http://wcc723.github.io/d3js/2014/10/21/Ironman-30-days-22/

資料很多時候需要比較、切換,所以可能會同時加入多筆資料,前一篇文章所介紹的是相同數量的資料,這次要做隨機的資料量。

資料轉換

資料數量不同時,如果增加到沒什麼,因為原本就是從無到有,都是利用Append去增加新的物件:

bars.enter().append('rect');

但是如果資料比原本還要少時,就要選取多餘的元件(exit),並且移除它(remove)。

bars.exit() //選擇多餘的部分
.remove() //清除物件

下面這個範例,預設會先用20筆資料,接下來點擊button,就會隨機的增減資料,另外動態做稍微誇張一些,覺得挺有趣的,玩了幾個動態模式,這個最彈了...。

var dataset = [];
var numValues = 20; //一開始的資料數量是固定的

var randDataset = function(num){
	for (var i=0; i < num; i++){
		var newNum = 5 + Math.floor(Math.random() * 30);
		dataset.push({ key : i, value: newNum}); //產生一段陣列,包含key and value
	}
}//只要給予資料"數量",就會產生隨機資料

var key = function(d) {
			return d.key;
		}

randDataset(numValues); //一開始預設的20筆資料

console.table(dataset); //打開console開結果

var originDataLength = dataset.length // 將原始的資料長度記錄

var h=200,w = 500, barMargin = 1;

maxDataset = d3.max(dataset, function(d){ return d.value }); //從value 取得最大值


var xScale = d3.scale.ordinal() 
//不同於linear的尺度,可以處理非數字資料
	.domain(d3.range(dataset.length)) //這範例是給予資料的長度
	.rangeRoundBands([0, w], 0); //利用整體的寬度去做運算

var yScale = d3.scale.linear() //y尺度和先前是相同的,都是線性尺度
		.domain([0 , maxDataset]) //輸入值範圍
		.range([0, h]); //輸出值範圍

var svg = d3.select('.demo').append('svg').attr('width',w).attr('height', h); //建立svg

svg.selectAll('rect').data(dataset, key) //插入資料
	.enter().append('rect') //直條圖用rect~
	.attr({
		'x': function(d, i){ 
			return xScale(i); //利用尺度算出每個rect的x軸位置
		},
		'y': function(d){ 
			return h - yScale(d.value); //算出y的位置(因為需要置底)
		},
		'width': w / dataset.length - barMargin, 
		//每個rect寬度是固定的,但需要補上間距
		'height': function(d){ return yScale(d.value)},
		//算出高度
		'fill': function(d){ return d3.hsl(320 + d.value  % 360 , .5 , .5)} //利用資料產生不同色彩
	});

var dataText = svg.selectAll('text').data(dataset, key).enter().append('text'); //補上文字
dataText.text(function(d){return d.value})
	.attr({
		'x': function(d,i){ 
			return xScale(i) + 11; //算出x位置
		},
		'y': function(d){ return (h - yScale(d.value) + 15)},
		//算出文字y的位置
		'fill': 'white', //文字反白
		'text-anchor': 'middle', //文字置中
		'font-size': '11px' //字小一點比較秀氣
	});



var chartFn = function(){ //這一段是在點擊後觸發的函式
	dataset = []; //資料清空
	var numValues = 1 + Math.floor(Math.random() * 30); //隨機長度的資料

	randDataset(numValues); //重新製作一組資料

	newDataLength = dataset.length // 儲存新的資料長度
	xScale.domain(d3.range(dataset.length)) 
	//資料長度不同,所以domain改變

	maxDataset = d3.max(dataset, function(d){ return d.value });
	yScale.domain([0 , maxDataset])
	//資料最大值不同,所以domain改變

	var bars = svg.selectAll("rect")
			.data(dataset, key); //重新套用資料

		var texts = svg.selectAll('text')
			.data(dataset, key);

		//檢查資料長度,如果比原本長就需要移除
	if (newDataLength > originDataLength){  //如果比原本長

		bars.enter().append('rect') 
			.transition()
			.attr('x', w); //增加新的rect,以及進場的方式

		texts.enter().append('text').text(function(d){return d.value})
			.attr({
				'x': w,
				'y': function(d){ return (h - yScale(d.value) + 15)}
			}); //類似同上
		originDataLength = newDataLength; //儲存資料長度

	} else if (newDataLength < originDataLength){
		bars.exit() //選擇多餘的部分
		.transition() //退場方式
		//.duration(500)
		.attr('width', 0) //寬度降為0
		.remove() //清除物件

		texts.exit().transition().duration(500)
		.attr('width', 0)
		.remove() //清除文字物件

		originDataLength = newDataLength; //儲存資料長度
	}


		bars.transition()
			.delay(500) //延遲新的效果(等前方的進退場動畫結束)
			// 	.delay(function(d, i) {
		// 	return i / dataset.length * 1000;   
		// // 每個物件套用不同的delay 時間
		// })
			.duration(1500) // 持續時間(豪秒)
			.ease('elastic') // 動畫型態
 			.attr({ //這段同前
			'x': function(d, i){ 
				return xScale(i) 
			},
			'y': function(d){ 
				return h - yScale(d.value) 
			},
			'width': w / dataset.length - barMargin,
			'height': function(d){
				return yScale(d.value);
			},
			'fill': function(d){ return d3.hsl(320 + d.value  % 360 , .5 , .5)}
		})
 	texts.transition() //文字也是同前
			.delay(500)
			.duration(1500) // 持續時間(豪秒)
			.ease('elastic') // 動畫型態
			.attr({
			'x': function(d,i){ 
				//return i * (w  / dataset.length) 
				return xScale(i) + 11
			},
			'y': function(d){ return (h - yScale(d.value) + 15)},
			'fill': 'white',
			'text-anchor': 'middle',
			'font-size': '11px'
		});


};

// 點擊button時,更新資料
d3.select('.update_btn').on('click', function(){
	chartFn();
});

接下來,明天又要來亂抓Open data來玩囉~


上一篇
D3.js Transition動態效果
下一篇
像jQuery 一樣的D3.js,柯P野生官網API
系列文
SVG 與 D3.js30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言